package com.example.demo.redis;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.List;

/**
 * redis分布式锁工具
 * 使用lua脚本实现加锁和解锁操作，保证原子性
 * @author liqingcan
 */
@Component
public class RedisLockService {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 加锁的lua脚本
     */
    private final static RedisScript<Long> LOCK_LUA_SCRIPT = new DefaultRedisScript<>(
            "if redis.call(\"setnx\", KEYS[1], KEYS[2]) == 1 then return redis.call(\"expire\", KEYS[1], KEYS[3]) else return 0 end"
            , Long.class
    );

    /**
     * 加锁失败结果
     */
    private final static Long LOCK_FAIL = 0L;

    /**
     * 解锁的lua脚本
     */
    private final static RedisScript<Long> UNLOCK_LUA_SCRIPT = new DefaultRedisScript<>(
            "if redis.call(\"get\",KEYS[1]) == KEYS[2] then return redis.call(\"del\",KEYS[1]) else return -1 end"
            , Long.class
    );

    /**
     * 解锁失败结果
     */
    private final static Long UNLOCK_FAIL = -1L;

    /**
     * 加锁方法
     * 对key加锁，value为key对应的值，expire是锁自动过期时间防止死锁
     * @param key key
     * @param value value
     * @param expire 锁自动过期时间(秒）
     * @return
     */
    public boolean lock(String key, String value, Long expire){
        if (key == null || value == null || expire == null) {
            return false;
        }
        List<String> keys = Arrays.asList(key, value, expire.toString());
        Long res = redisTemplate.execute(LOCK_LUA_SCRIPT, keys);
        return !LOCK_FAIL.equals(res);
    }

    /**
     * 解锁方法
     * 对key解锁，只有value值等于redis中key对应的值才能解锁，避免误解锁
     * @param key
     * @param value
     * @return
     */
    public boolean unlock(String key, String value){
        if (key == null || value == null) {
            return false;
        }
        List<String> keys = Arrays.asList(key, value);
        Long res = redisTemplate.execute(UNLOCK_LUA_SCRIPT, keys);
        return !UNLOCK_FAIL.equals(res);
    }

}
